package midas.mw.crt;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import midas.mw.cfg.CfgLogging;
import midas.mw.cfg.log.Logger;

/**
 * This class manages the relationship between JXTA identifiers and MIDAS
 * identifiers. It controls the information provided by the Central Node
 * about registered nodes. It also provides mapping between IP address and
 * MIDAS node identifiers
 *
 */
public class NodeIdentifierSolver
{
	/**
	 * This hashtable stores the information about nodes taking part of the
	 * MIDAS group in the network. It has the jxta identifiers mapped to 
	 * MIDAS node identifiers. JXTA identifier is unique and MIDAS node 
	 * identifier should be too
	 */
    private Hashtable jxtaConnectedPeers 		= null;
    
    /**
	 * This hashtable stores the information about nodes taking part of the
	 * MIDAS group in the network. It has the MIDAS node identifiers mapped 
	 * to IP addresses.
	 * A midas node could have several IP addresses. It will have the following
	 * format: "IP1:port;IP2:port;..."
	 */
    private Hashtable nodeIdToIpConnectedPeers	= null;
    
    /**
	 * This hashtable stores the information about nodes taking part of the
	 * MIDAS group in the network. It has the MIDAS node identifiers mapped 
	 * to its proxy
	 */
    private Hashtable nodeIdToProxy	= null;
    
    /**
	 * This hashtable stores the information about nodes taking part of the
	 * MIDAS group in the network. It has the MIDAS node identifiers of the User 
	 * Nodes mapped to the MIDAS node identifiers of the Central Nodes they are
	 * connected to
	 */
    private Hashtable nodeIdToCentralNode	= null;
    
    /**
	 * This hashtable stores the information about nodes taking part of the
	 * MIDAS group in the network. It has the MIDAS node identifiers of the 
	 * Central Nodes (or Gateway Nodes) mapped to the Central Node they are 
	 * connected to
	 */
    private Hashtable gatewayNodes	= null;
    
    /**
	 * Instance of the CRT running in the node
	 */
    private CrtImpl crt						= null;
	
    /**
	 * Constructor - initiate local variables
	 * 
	 * @param crt Instance of the CRT running in the node
	 */
	public NodeIdentifierSolver(CrtImpl crt)
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver(CrtImpl)");
		
		this.crt 					= crt;
		jxtaConnectedPeers 			= new Hashtable();
		nodeIdToIpConnectedPeers 	= new Hashtable();
		nodeIdToProxy 				= new Hashtable();
		nodeIdToCentralNode 		= new Hashtable();
		gatewayNodes 				= new Hashtable();
	} 
	
	/**
	 * Provides a hashtable where is stored the information about the nodes 
	 * taking part of the MIDAS group in the network
	 * 
	 * @return Hashtable with the JXTA identifiers of the nodes taking part of the 
	 * 			MIDAS group in the network mapped to MIDAS nodes identifiers
	 */
	public Hashtable getJxtaConnectedPeers()
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.getJxtaConnectedPeers()");		
		
		return jxtaConnectedPeers;
	}
	
	/**
	 * Provides a hashtable where is stored the information about the nodes 
	 * taking part of the MIDAS group in the network
	 * 
	 * @return Hashtable with the MIDAS node identifiers of the nodes taking part of the 
	 * 			MIDAS group in the network mapped to IP addresses. The string stoting the
	 * 			IP addresses will have the following format: "IP1:port;IP2:port;..."
	 */
	public Hashtable getNodeIdToIpConnectedPeers()
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.getNodeIdToIpConnectedPeers()");		
		
		return nodeIdToIpConnectedPeers;
	}
	
	/**
	 * Provides the IP addresses of a MIDAS node from its MIDAS node identifier. If
	 * the node is working in Ad-hoc mode with the wireless interface, it could
	 * be more interesting to use the function "getIPAddress(String nodeId, boolean isAvailable)"
	 * from the NetworkManager component
	 * 
	 * @param nodeId MIDAS node identifier
	 * @return String storing the IP addresses of the node. It will have the 
	 * 			following format: "IP1:port;IP2:port;..."; null if the MIDAS
	 * 			node identifier is not mapped to any value
	 */
	public String getIPAddress(String nodeId)
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.getIPAddress(" + nodeId + ")");		
		
		return (String)nodeIdToIpConnectedPeers.get(nodeId);
	}
	
	/**
	 * Returns information specifying if a node is a Nokia node or not  
	 * @param nodeId MIDAS node identifier
	 * @return true if is a Nokia node; false otherwise
	 */
	public boolean isJXMENode(String nodeId)
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.isJXMENode(" + nodeId + ")");	
		
		boolean isJxme = false;			
		
		if (nodeIdToProxy.containsKey(nodeId))
			isJxme = true;
		
		return isJxme;
	}
	
	/**
	 * Stores the pair MIDAS node identifier - IP adddress in the hashtable
	 * 
	 * @param nodeId MIDAS node identifier
	 * @param ipAddress String storing the IP addresses of the node. It will 
	 * 			have the following format: "IP1:port;IP2:port;..." 
	 * @return The previous IP address of this MIDAS node in the hashtable, 
	 * 			or null if it did not have one. 
	 */
	public String setIPAddress(String nodeId, String ipAddress)
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.setIPAddress(" + nodeId + ", " + ipAddress + ")");		
		
		return (String)nodeIdToIpConnectedPeers.put(nodeId, ipAddress);
	}
	
	/**
	 * Provides the MIDAS node identifier of MIDAS node from its JXTA unique 
	 * identifier
	 * 
	 * @param jxtaIdentifier JXTA identifier
	 * @return The MIDAS node identifier; null if the Jxta identifier is not 
	 * 			mapped to any value
	 */
	public String getNodeId(String jxtaIdentifier)
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.getNodeId(" + jxtaIdentifier + ")");		
		
		return (String)jxtaConnectedPeers.get(jxtaIdentifier);
	}
	
	/**
	 * Provides the MIDAS node identifier of MIDAS node from its IP address. If
	 * the node is working in Ad-hoc mode with the wireless interface, it could
	 * be more interesting to use the function "getNodeIdentifier(String ipAddress)"
	 * from the NetworkManager component
	 * 
	 * @param ipAddress Node IP address
	 * @return The MIDAS node identifier; null if the IP address is not mapped
	 * 			to any value
	 */
	public String getMIDASNodeId(String ipAddress)
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.getMIDASNodeId(" + ipAddress + ")");		
		
		for (Enumeration e = nodeIdToIpConnectedPeers.keys(); e.hasMoreElements() ;) 
		{
			String midasNodeId = (String)e.nextElement();
			String ipAdd = (String)nodeIdToIpConnectedPeers.get(midasNodeId);
			if (ipAdd.indexOf(ipAdd) != -1)
				return midasNodeId;
		}
		return null;
	}
	
	/**
	 * Stores the pair JXTA identifier - MIDAS node identifier in the hashtable
	 * 
	 * @param jxtaIdentifier JXTA identifier
	 * @param nodeId MIDAS node identifier
	 * @return The previous MIDAS node identifier of this MIDAS node in the 
	 * 			hashtable, or null if it did not have one.
	 */
	public String setJxtaIdentifier(String jxtaIdentifier, String nodeId)
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.setJxtaIdentifier(" + jxtaIdentifier + ", " + nodeId + ")");				
		
		return (String)nodeIdToIpConnectedPeers.put(jxtaIdentifier, nodeId);
	}
	
	/**
	 * It allows to store different information about a node using different hashtables
	 * It can store the pairs:
	 * <ul>JXTA identifier - MIDAS node identifier
	 * <ul>MIDAS node identifier - IP adddress
	 * <li>MIDAS node identifier - Proxy name
	 * <li>MIDAS node identifier - Central Node name
	 * </ul>
	 * @param jxtaIdentifier JXTA identifier
	 * @param nodeId MIDAS node identifier
	 * @param nodeType MIDAS node type
	 * @param proxyName MIDAS node identifier of the proxy node
	 * @param ipAddress String storing the IP addresses of the node. It will
	 * 			have the following format: "IP1:port;IP2:port;..."
	 * @return The previous MIDAS node identifier of this MIDAS node in the 
	 * 			hashtable, or null if it did not have one.
	 */
	public String put(String jxtaIdentifier, String nodeId, int nodeType, String proxyName, String ipAddress)
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.put(" + jxtaIdentifier + ", " + nodeId 
				+ ", " + nodeType + ", " + proxyName + ", " + ipAddress + ")");		
		
		String nodeName = (String)jxtaConnectedPeers.put(jxtaIdentifier, nodeId);	
		nodeIdToIpConnectedPeers.put(nodeId, ipAddress);
		if (nodeType == Constants.NOKIA_USER_NODE)	
			nodeIdToProxy.put(nodeId, proxyName);
		if (nodeType == Constants.USER_NODE && 
				(crt.getConstants().getNodeType() == Constants.CENTRAL_NODE ||					
				 crt.getConstants().getNodeType() == Constants.GATEWAY_NODE))	
			nodeIdToCentralNode.put(nodeId, proxyName);
		else if ((nodeType == Constants.CENTRAL_NODE || 
				nodeType == Constants.GATEWAY_NODE) && proxyName != null)	
			gatewayNodes.put(nodeId, proxyName);
			
		//Updating the wireless routing table if it is necessary
		int index = -1;
		
		boolean isInAdHocMode = false;
    	try {
    		isInAdHocMode = crt.getNetworkManager().isInAdHocMode();
    	} catch (CrtException crtException){
    		crtException.printStackTrace();
    	}
    	
		if ((index = ipAddress.indexOf("192.168.100.")) != -1 && isInAdHocMode)
		{
			String wirelessIPAddress = ipAddress.substring(index, ipAddress.indexOf(";", index));
			crt.getNetworkManager().addEntryRoutingTable(nodeId, wirelessIPAddress);
		}
		return nodeName;
	}
	
	/**
	 * Removes the JXTA identifier (and its corresponding value) and the MIDAS
	 * node identifier (and its corresponding value) from the appropiate hashtables
	 *  
	 * @param jxtaIdentifier JXTA identifier
	 * @return The MIDAS node identifier to which the JXTA identifier had been mapped,
	 * 			or null if it did not have a mapping. 
	 */
	public String remove(String jxtaIdentifier)
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.remove(" + jxtaIdentifier  + ")");		
		
		String nodeId = (String)jxtaConnectedPeers.remove(jxtaIdentifier);
		if (nodeId != null)
		{
			nodeIdToIpConnectedPeers.remove(nodeId);
			nodeIdToProxy.remove(nodeId);
			nodeIdToCentralNode.remove(nodeId);
			gatewayNodes.remove(nodeId);
		}
		return nodeId;
	}
	
	/**
	 * Clears all hashtables so they contains no keys
	 */
	public void stopNodeIdentifierSolver()
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.stopNodeIdentifierSolver()");
		
		jxtaConnectedPeers.clear();
		nodeIdToIpConnectedPeers.clear();
		nodeIdToProxy.clear();
		nodeIdToCentralNode.clear();
		gatewayNodes.clear();
	}
	
	/**
	 * Returns all the Nokia nodes associated to this Central Node
	 * @return A vector storing the MIDAS node identifier of all the Nokia 
	 * 			nodes associated to this Central Node
	 */
	public Vector getOwnJXMENodes()
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.getOwnJXMENodes()");
		
		Vector jxmeNodes = new Vector();
		String nodeName  = null;
		for (Enumeration e = nodeIdToProxy.keys();e.hasMoreElements();)
		{
			nodeName = (String)e.nextElement();
			if (((String)nodeIdToProxy.get(nodeName)).equals(crt.getOwnNodeId()))	
				jxmeNodes.add(nodeName);
		}
		return jxmeNodes;
	}
	
	/**
	 * Returns all the non-Nokia nodes associated to this Central Node
	 * @return A vector storing the MIDAS node identifier of all the non-Nokia 
	 * 			nodes associated to this Central Node
	 */
	public Vector getOwnNormalNodes()
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.getOwnNormalNodes()");
		
		Vector nodes = new Vector();
		String nodeName  = null;
		for (Enumeration e = nodeIdToCentralNode.keys();e.hasMoreElements();)
		{
			nodeName = (String)e.nextElement();
			if (((String)nodeIdToCentralNode.get(nodeName)).equals(crt.getOwnNodeId()))	
				nodes.add(nodeName);
		}
		return nodes;
	}
	
	/**
	 * Returns information about all the Central/Gateway Nodes in the network
	 * @return A hashtable storing information that maps the MIDAS node identifier of all Central/Gateway Nodes 
	 * in the network to the proxies they are connected to
	 */
	public Hashtable getGatewayNodes()
	{
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.getGatewayNodes()");
		
		return gatewayNodes;
	}
	
	/**
	 * Returns information about all the Nokia nodes in the network
	 * @return A hashtable storing information that maps the MIDAS node identifier of all Nokia in the network
	 * 			to the proxies they are connected to
	 */
	public Hashtable getAllJXMENodes()
	{	
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, Logger.COMPONENT_CRT, CfgLogging.SEV_DEBUG, "NodeIdentifierSolver.getAllJXMENodes()");
		
		return nodeIdToProxy;
	}	
}


